package com.mzj.saas.mservice.smartlock.sgin;

import com.alibaba.fastjson.JSON;
import com.mzj.saas.sdk.util.Md5Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

public class RSAUtils {

	private static final Logger log = LoggerFactory.getLogger(RSAUtils.class);
	
	private final static int KEY_SIZE = 2048;
	
	private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";

	public static String createAccessKey() {
		return UUID.randomUUID().toString().replace("-", "");
	}

	/**
	 * 采用RAS2048方式生成公私钥
	 * 
	 * @return
	 * @throws NoSuchAlgorithmException
	 */
	public static Map<String, String> createRSA() throws NoSuchAlgorithmException {

		Map<String, String> resultMap = new HashMap<String, String>();

		// 生成公钥和私钥对，基于RSA算法生成对象
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
		// 初始化密钥对生成器
		keyPairGen.initialize(KEY_SIZE, new SecureRandom());
		// 生成一个密钥对，保存在keyPair中
		KeyPair keyPair = keyPairGen.generateKeyPair();
		// 私钥
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		// 公钥
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		// 公钥字符串
		String publicKeyString = Base64.getEncoder().encodeToString(publicKey.getEncoded());

		// 私钥字符串
		String privateKeyString = Base64.getEncoder().encodeToString(privateKey.getEncoded());

		resultMap.put("publicKey", publicKeyString);
		resultMap.put("privateKey", privateKeyString);

		return resultMap;
	}

	/**
	 * 使用私钥对数据进行加密签名
	 * 
	 * @param data
	 *            数据
	 * @param privateKeyString
	 *            私钥
	 * @return 加密后的签名
	 */
	public static String sign(String data, String privateKeyString) throws Exception {
		KeyFactory keyf = KeyFactory.getInstance("RSA");
		PrivateKey privateKey = keyf
				.generatePrivate(new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyString)));
		Signature signet = Signature.getInstance(SIGNATURE_ALGORITHM);
		signet.initSign(privateKey);
		signet.update(data.getBytes("utf-8"));
		byte[] signed = signet.sign();
		return Base64.getEncoder().encodeToString(signed);
	}

	/**
     * 获取私钥
     *
     * @param privateKey 私钥字符串
     * @return
     */
    public static PrivateKey getPrivateKey(String privateKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.getDecoder().decode(privateKey.getBytes());
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(decodedKey);
        return keyFactory.generatePrivate(keySpec);
    }

    /**
     * 获取公钥
     *
     * @param publicKey 公钥字符串
     * @return
     */
    public static PublicKey getPublicKey(String publicKey) throws Exception {
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        byte[] decodedKey = Base64.getDecoder().decode(publicKey.getBytes());
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(decodedKey);
        return keyFactory.generatePublic(keySpec);
    }

    /**
     *
     * @param data
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String signater(String data, PrivateKey privateKey) throws Exception {
	    Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
	    signature.initSign(privateKey);
	    signature.update(data.getBytes("UTF-8"));
	    return Base64.getEncoder().encodeToString(signature.sign());
    }


    public static boolean verify(String data, String sign,PublicKey publicKey) throws Exception {
    	Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
    	signature.initVerify(publicKey);
    	signature.update(data.getBytes());
    	return signature.verify(Base64.getDecoder().decode(sign));
    }

	/**
     * 签名
     *
     * @param data 待签名数据
     * @param privateKey 私钥
     * @return 签名
     */
    public static String sign(String data, PrivateKey privateKey) throws Exception {
        byte[] keyBytes = privateKey.getEncoded();
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PrivateKey key = keyFactory.generatePrivate(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(key);
        signature.update(data.getBytes());
        return Base64.getEncoder().encodeToString(signature.sign());
    }

    /**
     * 验签
     *
     * @param srcData 原始字符串
     * @param publicKey 公钥
     * @param sign 签名
     * @return 是否验签通过
     */
    public static boolean verify(String srcData, PublicKey publicKey, String sign) throws Exception {
        byte[] keyBytes = publicKey.getEncoded();
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        PublicKey key = keyFactory.generatePublic(keySpec);
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initVerify(key);
        signature.update(srcData.getBytes());
        return signature.verify(Base64.getDecoder().decode(sign.getBytes()));
    }


	/**
	 * 使用公钥判断签名是否与数据匹配
	 *
	 * @param data
	 *            数据
	 * @param sign
	 *            签名
	 * @param publicKeyString
	 *            公钥
	 * @return 是否篡改了数据
	 */
	public static boolean verify(String data, String sign, String publicKeyString) throws Exception {
		KeyFactory keyf = KeyFactory.getInstance("RSA");
		PublicKey publicKey = keyf.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyString)));
		Signature signet = Signature.getInstance(SIGNATURE_ALGORITHM);
		signet.initVerify(publicKey);
		signet.update(data.getBytes("utf-8"));
		return signet.verify(Base64.getDecoder().decode(sign));
	}
	
	
	/**
	 * 对参数的值拼接MD5计算，转为大写
	 * @param timestamp
	 * @param accesskey
	 * @param randomStr
	 * @param securitykey
	 */
	public static String sign(String timestamp,String accesskey,String randomStr,String securitykey) {
		
		String stringSignTemp = timestamp + accesskey + randomStr + securitykey;
		
		log.info("加密数据 = {}",stringSignTemp);
		
		return Md5Utils.MD5Encode(stringSignTemp, "UTF-8").toUpperCase();
		
	}
	

	/**
	 * 遍历字符串数据,组装数据
	 * 将请求参数中的字段，按照字段名字典序升序排列，并将字段值进行首尾相接，如果是多层嵌套结构则按照递归升序进行排列拼接，拼接完成后在字符串末尾加入时间戳（timestamp）。
	 * @param result
	 * @param
	 * @return
	 */
	public static String convertData(String result) {
		try {
			Map mapTypes = JSON.parseObject(result);
			SortedMap<Object, Object> param = new TreeMap<Object, Object>();
			jointToValue(param, mapTypes);
			StringBuffer sb = new StringBuffer();
			Set<Map.Entry<Object, Object>> es = param.entrySet();
			Iterator<Map.Entry<Object, Object>> it = es.iterator();
			while (it.hasNext()) {
				Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();
				String k = (String) entry.getKey();
				Object v = entry.getValue();
				if(v.toString().indexOf("[") > -1) {
					sb.append(v.toString().replaceAll("\\[","").replaceAll("\\]", "").replaceAll(",", "").replaceAll("\"", ""));
				}else if (!"".equals(v) && !"sign".equals(k) && !"timestamp".equals(k)) {
					sb.append(v);
				}
			}
			sb.append(param.get("timestamp"));
			return sb.toString();
		}catch(Exception e) {
			log.error(e.getMessage(),e);
		}
		return null;
	}

	/**
	 * 递归数据
	 * @param param
	 * @param mapTypes
	 */
	public static void jointToValue(SortedMap<Object, Object> param, Map mapTypes) {
		if (mapTypes == null || mapTypes.isEmpty()) {
			return;
		}
		Map mapVaue = null;
		for (Object obj : mapTypes.keySet()) {
			if (mapTypes.get(obj) == null) {
				continue;
			}
			String value = mapTypes.get(obj).toString();
			if (value.indexOf("{") == -1) {
				param.put(obj, value);
				continue;
			}
			
			
			
			mapVaue = JSON.parseObject(value);
			jointToValue(param, mapVaue);
		}
		
	}

	/**
	 * 对参数的值拼接MD5计算
	 * @param sid
	 * @param xTimestamp
	 * @param xNonce
	 * @param sercret
	 * @return
	 * @author shun
	 * @date 2021年6月26日
	 */
	public static String xzSign(String sid, String xTimestamp, String xNonce, String sercret) {
		String stringSignTemp = sid + xTimestamp + xNonce + sercret;
		log.info("加密数据 = {}",stringSignTemp);
		return Md5Utils.MD5Encode(stringSignTemp, "UTF-8");
		
	}
}
